Lotto Insights

by CM


Posted on January 14, 2022



The Goal:

In this article, we will build a ReactJS application aka. a “Drinking Game”. The game will be powered with ReactJS on the front-end using reusable-components and state-management. On the backend, we will make use of Firestore (a no-SQL database that comes with Firebase). Together, we have a dynamic application that allows us to read states and write states to a database. The idea here is that our 'Drinking Game' will be playable on multiple devices at the time respectively all Players entering the same game. The rules will be pretty simple: (1) Select a number of BLOCKS (2) Select a number of #BOMBS hidden in the #BLOCKS (3) Select a number of #SHOTS hidden in the #BLOCKS. Shots and Bombs are distributed randomly. Whenever you unhide a #BOMB, you need to do 10 push-ups. Whenever you unhide a #SHOT, you need to drink a shot. Regarding the code, we will build the entire application using JavaScript, HTML, CSS with some Bootstrap. Lastly, we will deploy our application on Firebase hosting and use Firestore to store the game data, such as blocks state, bomb and shots arrays, clicks and player names.


Key components are:

Resources:

Let's jump right into the Code. React App usually consists of a static index.html file, some css, as well as a source folder that holds all the components. Thus, we will make use of components. In other words, we build reusable UI components that will later power our application. In addition, we will use Firebase for hosting and Firestore as a database. Lastly, we will add some images and sounds to the game. Let's have a look at our folder structure:


Our index.html file - just hold a root-div as an anchor point for the react application. Besides that we only add bootstrap for design purposes.

<!DOCTYPE html>
<html lang="en">
<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
  integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
  crossorigin="anonymous">

  <title>React App</title>
</head>

<body>

  <div id="root"></div>

</body>
</html>

Second, we will start building our React APP. We will have an APP.js file that references all components. Within the file, we start importing all components that will power our game, as well as the Firebase and ReactJS state management dependencies.

import './index.css'
import { useState, useEffect } from 'react';
import Field_2 from './components/Field_2';
import Blocks_Menu from './components/Blocks_Menu';
import Bombs_Menu from './components/Bombs_Menu';
import Shots_Menu from './components/Shots_Menu';
import Begin_Button from './components/Begin_Button';
import Start_Button from './components/Start_Button';
import Restart_Button from './components/Restart_Button';
import Reset_Button from './components/Reset_Button';
import Join_Button from './components/Join_Button';
import Refresh_Button from './components/Refresh_Button';
import Player_Name from './components/Player_Name';
import Back_Button from './components/Back_Button';
import PlayAlone from './components/PlayAlone_Checkbox';
import useSound from 'use-sound';
import start_game_sound from './components/sound/start_game.mp3';
import power_up_sound from './components/sound/power_up.mp3'
import back_sound from './components/sound/back.mp3'
import refresh_sound from './components/sound/refresh.mp3'
import turn_sound from './components/sound/turn.mp3';
import resetGameFunction from "./components/Reset_Function"
import db from "./firebase"
import {onSnapshot, doc, collection, addDoc, setDoc} from "firebase/firestore";

Third, we can already start writing our game functions. In this regard, we set the individual hooks, variables and logs for debugging.

function App() {

//##############################################################################
// Getting Firestore Data
//##############################################################################
useEffect(() =>
onSnapshot(collection(db, "game_state"), (snapshot) => {
  setGame_State(snapshot.docs.map(doc=> doc.data()));
 }
),
[],
);

//##############################################################################
// Creating unique ID for Fields
//##############################################################################
let id = 0;
function idProp() {id = id+1; return (id)};

//##############################################################################
//Set Blocks | Bombs
//#############################################################################
const [game_state, setGame_State] = useState([]);
let live_game_state_array = [];
let live_game_state_array_ids = [];
let live_game_state_array_bombs = [];
let live_game_state_array_shots = [];
let live_game_state_array_block_x = [];
let live_game_state_array_game_id = [];
let live_game_state_array_playername = [];
let live_game_state_array_game_clicks = [];
let live_game_state_array_ever_clicks = [];
//console.log(game_state);
//console.log("GAMESTATE: "+game_state.map(block=> (live_game_state_array.push(block.state))));
game_state.map(block=> (live_game_state_array.push(block.state)));
game_state.map(block=> (live_game_state_array_ids.push(block.id)));
game_state.map(block=> (live_game_state_array_bombs.push(block.bombs)));
game_state.map(block=> (live_game_state_array_shots.push(block.shots)));
game_state.map(block=> (live_game_state_array_block_x.push(block.blocks)));
game_state.map(block=> (live_game_state_array_game_id.push(block.id)));
game_state.map(block=> (live_game_state_array_playername.push(block.playername)));
game_state.map(block=> (live_game_state_array_game_clicks.push(block.clicks)));
game_state.map(block=> (live_game_state_array_ever_clicks.push(block.ever_clicks)));
console.log("Bombs in database: "+live_game_state_array_bombs[101]);
console.log("Shots in database: "+live_game_state_array_shots[102]);
console.log("Blocks in database: "+live_game_state_array_block_x[100]);
console.log("Game ID: "+live_game_state_array_game_id[103]);
console.log("Last Clicker: "+live_game_state_array_playername[104]);
console.log("Game Clicks: "+live_game_state_array_game_clicks[105]);
console.log("Ever Clicks: "+live_game_state_array_ever_clicks[105]);
console.log("==================================================================");

let default_blocks = 10;
let default_bombs = [Math.floor(Math.random() * 10+1)];
let default_shots =  [Math.floor(Math.random() * 10+1)];
let default_blocks_db = live_game_state_array_block_x[100];
let default_bombs_db = live_game_state_array_bombs[101];
let default_shots_db = live_game_state_array_shots[102];
let default_game_id_db = live_game_state_array_game_id[103];
let default_playername_db = live_game_state_array_playername[104];
let default_game_clicks_db = live_game_state_array_game_clicks[105];
let default_ever_clicks_db = live_game_state_array_ever_clicks[105];

console.log(default_blocks_db);
console.log(default_bombs_db);
console.log(default_shots_db);
console.log(default_game_id_db);
console.log(default_playername_db);

const [blocks, setBlocks] = useState();
const [bombs, setBombsArray] = useState();
const [shots, setShotsArray] = useState();
const [game_id, setGameIDArray] = useState();
const [start, setStart] = useState(0);
const [playername, setPlayerName] = useState("Player");
const [playAlone, setPlayAlone] = useState(true);

const [play_start_game] = useSound(start_game_sound);
const [sound_power_up_sound] = useSound(power_up_sound);
const [sound_back_sound] = useSound(back_sound);
const [sound_refresh_sound] = useSound(refresh_sound);
const [sound_turn_sound] = useSound(turn_sound);

Next, we need to build two functions for creating the bombs and the shots. In this regard, we initialize an array and push random numbers into it based on the user's input. Further, we make sure that each bomb and shot has a unique number.

//##############################################################################
//Create Bombs
//##############################################################################
function createBombsArray(bombs_x, blocks_x){
console.log("createBombsArray: "+ bombs_x);
console.log("useBlocks: "+ blocks_x);
if (+bombs_x > +blocks_x) {

  alert("You cannot have more bombs than Blocks");
bombs_x = blocks_x;
let arr = [];
while(arr.length < bombs_x){
  var r = Math.floor(Math.random() * blocks_x) + 1;
  if(arr.indexOf(r) === -1) arr.push(r);
} return arr

}
 else {

let arr = [];
while(arr.length < bombs_x){
    var r = Math.floor(Math.random() * blocks_x) + 1;
    if(arr.indexOf(r) === -1) arr.push(r);
 }
 return arr}
};

//##############################################################################
//Create Shots
//##############################################################################
function createShotsArray(shots_x, blocks_x){
console.log("createBombsArray: "+ shots_x);
console.log("useBlocks: "+ blocks_x);
if (+shots_x > +blocks_x) {

  alert("You cannot have more bombs than Blocks");
shots_x = blocks_x;
let shot_arr = [];
while(shot_arr.length < shots_x){
  var shot_r = Math.floor(Math.random() * blocks_x) + 1;
  if(shot_arr.indexOf(shot_r) === -1) shot_arr.push(shot_r);
} return shot_arr

}
 else {

let shot_arr = [];
while(shot_arr.length < shots_x){
    var shot_r = Math.floor(Math.random() * blocks_x) + 1;
    if(shot_arr.indexOf(shot_r) === -1) shot_arr.push(shot_r);
 } return shot_arr}
};

After that, we build six handlers regarding (1) Game state, (2) Bombs setting, (3) Shots setting, (4) Player name, (5) Sound, (6) Playing alone Option.

//##############################################################################
//Set Blocks Fun & Bomb Fun
//##############################################################################
function handleBlocks(event){
setBlocks(event.target.value);
console.log("Blocks Menu Activated with: " + event.target.value);
startGame();
//setBombsArray(createBombsArray(event.target.value));
}

function handleBombs(event2){
console.log("Bombs Menu Activated with: " + event2.target.value);
console.log("blocks with: "+blocks);
setBombsArray(createBombsArray(event2.target.value, blocks));
startGame();
}

function handleShots(event3){
console.log("Shots Menu Activated with: " + event3.target.value);
console.log("blocks with: "+blocks);
setShotsArray(createShotsArray(event3.target.value, blocks));
startGame();
}

function handleName(event4){
console.log("Name Activated with: " + event4.target.value);
setPlayerName(event4.target.value);
}

function handleBack(){
setStart(start - 1 );
sound_back_sound();
}

function handlePlayAlone(){
console.log("PlayAlone Status: " + playAlone);
if (playAlone==true) {setPlayAlone(false)} else {setPlayAlone(true)};
console.log("PlayAlone Status: " + playAlone);
sound_back_sound();
}

Next, we set up an array to keep track of the clicked blocks.

const [clickedArray, setClickedArray] = useState([]);

Now it is finally time to build that startGame function. Based on the gameState, we show and hide individual UI components. Further, we set our background based on the game-state-id for visual effects.

//##############################################################################
//Start game
//##############################################################################
function startGame() {
setStart(start + 1 );
console.log(start);

if (start!==4){sound_power_up_sound();}

//Setting bombs array in FIRESTORE
if (start===4){
resetGame();



setDoc(doc(db, "game_state", "bombs"), {bombs: bombs});
setDoc(doc(db, "game_state", "shots"), {shots: shots});
setDoc(doc(db, "game_state", "block_x"), {blocks: blocks});

let timestamp_for_game_id=Date.now();
setDoc(doc(db, "game_state", "xyz_game_id"), {id:timestamp_for_game_id});
console.log("TIME---------------"+timestamp_for_game_id.toString());

play_start_game();

if (timestamp_for_game_id % 10 == 0)
 {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
 if (timestamp_for_game_id % 10 == 1)
  {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
  if (timestamp_for_game_id % 10 == 2)
   {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
   if (timestamp_for_game_id % 10 == 3)
    {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
    if (timestamp_for_game_id % 10 == 4)
     {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/3.jpg')";};
     if (timestamp_for_game_id % 10 == 5)
      {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/3.jpg')";};
      if (timestamp_for_game_id % 10 == 6)
       {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/3.jpg')";};
       if (timestamp_for_game_id % 10 == 7)
        {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/9.jpg')";};
        if (timestamp_for_game_id % 10 == 8)
         {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/9.jpg')";};
         if (timestamp_for_game_id % 10 == 9)
          {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/9.jpg')";};
};
}

As mentioned in the description, we want to allow other Players to JOIN the game. Hence, we build a joinGame function including a refresh button in case a new game gets created while playing.

//##############################################################################
//Join game
//##############################################################################
function joinGame() {
setStart(start + 5 );
console.log("join");

play_start_game();

//Setting bombs array in FIRESTORE
setBlocks(default_blocks_db);
setBombsArray(default_bombs_db);
setShotsArray(default_shots_db);
setGameIDArray(default_game_id_db);

let timestamp_for_game_id=Date.now();
setDoc(doc(db, "game_state", "xyz_game_id"), {id:timestamp_for_game_id});
console.log("TIME---------------"+timestamp_for_game_id.toString());

if (default_game_id_db % 10 == 0)
{document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
if (default_game_id_db % 10 == 1)
{document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
if (default_game_id_db % 10 == 2)
 {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
 if (default_game_id_db % 10 == 3)
  {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/2.jpg')";};
  if (default_game_id_db % 10 == 4)
   {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/3.jpg')";};
   if (default_game_id_db % 10 == 5)
    {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/3.jpg')";};
    if (default_game_id_db % 10 == 6)
     {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/3.jpg')";};
     if (default_game_id_db % 10 == 7)
      {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/9.jpg')";};
      if (default_game_id_db % 10 == 8)
       {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/9.jpg')";};
       if (default_game_id_db % 10 == 9)
        {document.body.style.backgroundImage="url('https://storage.googleapis.com/epicml_public_bucket/Crasher_game_background/9.jpg')";};

}

//##############################################################################
//Join game
//##############################################################################
function refreshGame() {
setStart(5);
console.log("refresh");

//Setting bombs array in FIRESTORE
setBlocks(default_blocks_db);
setBombsArray(default_bombs_db);
setShotsArray(default_shots_db);
setGameIDArray(default_game_id_db);
sound_refresh_sound();
}


//##############################################################################
//Reset game
//##############################################################################
function resetGame(){
resetGameFunction(default_ever_clicks_db)};

Lastly, we will build an array for setting our blocks in the game.

//##############################################################################
//Create Field Array
//##############################################################################
let field_array = [];
for (let step = 0; step < blocks; step++) {
field_array.push(<Field_2
  blocks={blocks}
  id={idProp()}
  clickedArray={clickedArray}
  setClickedArray={setClickedArray}
  bombs={bombs}
  shots={shots}
  playername={playername}

  setBombsArray={setBombsArray}
  setShotsArray={setShotsArray}
  setBlocks={setBlocks}

  playAlone={playAlone}
  default_game_clicks_db={default_game_clicks_db}

  game_clicks={default_game_clicks_db}
  ever_clicks={default_ever_clicks_db}
  default_playername_db={default_playername_db}

  key={id}
  live_game_state_array={live_game_state_array}
  live_game_state_array_ids={live_game_state_array_ids}

  />);
};

In the final step of our APP.js file, we will return the JSX.

return (

<div style={{textAlign:'center', color:'white'}} id="all_JSX_________________________________________________________">
  <h1 className="heading_top">Crasher Game</h1>

{start===0 && <Player_Name  playername={playername} setPlayerName={setPlayerName} handleName={handleName} handlePlayAlone={handlePlayAlone} playAlone={playAlone}  />}
{start===0 && <Start_Button startGame={startGame} />}
{start===0 && <Join_Button setShotsArray={setShotsArray} setBombsArray={setBombsArray} joinGame={joinGame}/>}
{start===0 && <Reset_Button resetGame={resetGame} setShotsArray={setShotsArray}  setBombsArray={setBombsArray} />}

{start===1 && <Blocks_Menu setBlocks={setBlocks} blocks={blocks} handleBlocks={handleBlocks} startGame={startGame} />}
{start===1 && <Back_Button handleBack={handleBack} />}

{start===2 && <Bombs_Menu  setBombsArray={setBombsArray} bombs={bombs}  blocks={blocks} handleBombs={handleBombs} startGame={startGame} />}
{start===2 && <Back_Button handleBack={handleBack} />}

{start===3 && <Shots_Menu  setBombsArray={setBombsArray} blocks={blocks} bombs={bombs} handleShots={handleShots} setShotsArray={setShotsArray} shots={shots} startGame={startGame}/>}
{start===3 && <Back_Button handleBack={handleBack} />}

{start===4 && <Begin_Button startGame={startGame} resetGame={resetGame} blocks={blocks} bombs={bombs}  shots={shots}/>}
{start===4 && <Back_Button handleBack={handleBack} />}

{start===5 && <p className="game_id">Game-ID: {default_game_id_db}</p> }
{start===5 && <Restart_Button setStart={setStart} setClickedArray={setClickedArray} setBombsArray={setBombsArray} />}
{start===5 && <Refresh_Button setShotsArray={setShotsArray} setBombsArray={setBombsArray} refreshGame={refreshGame}/>}
{start===5 &&

    <div className="row justify-content-center">

     {field_array}

     <h5>-----</h5>
     <h4>Total Game Clicks: {default_game_clicks_db}</h4>
     <h5>Total Ever Clicks: {default_ever_clicks_db}</h5>
     <h4>Last Turn: {default_playername_db}</h4>

    </div>
    }
</div>
);
}
export default App;

The individual component and well as the database connection can be found in the GitHub Repo in the respective src folder.

Here is the final application: Final Application.


Happy Playing

#EpicML


News
Dec 2021

--- Quantum ---

Simulating matter on the quantum scale with AI #Deepmind
Nov 2021

--- Graviton3 ---

Amazon announced its Graviton3 processors for AI inferencing - the next generation of its custom ARM-based chip for AI inferencing applications. #Graviton3
May 2021

--- Vertex AI & TPU Gen4. ---

Google announced its fourth generation of tensor processing units (TPUs) for AI and ML workloads and the Vertex AI managed platform #VertexAI #TPU
Feb 2021

--- TensorFlow 3D ---

In February of 2021, Google released TensorFlow 3D to help enterprises develop and train models capable of understanding 3D scenes #TensorFlow3D
Nov 2020

--- AlphaFold ---

In November of 2020, AlphaFold 2 was recognised as a solution to the protein folding problem at CASP14 #protein_folding
Oct 2019

--- Google Quantum ---

A research effort from Google AI that aims to build quantum processors and develop novel quantum algorithms to dramatically accelerate computational tasks for machine learning. #quantum_supremacy
Oct 2016

--- AlphaGo ---

Mastering the game of Go with Deep Neural Networks. #neural_network